﻿using Pay.Core;
using Pay.Data;
using Pay.DTO;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Text;

namespace Pay.Service {
    public class Repository : IUnitOfWork, IDisposable {
        private readonly object sync = new object();
        private List<string> cacheTags = new List<string>();
        /// <summary>
        /// 是否启用分布事务
        /// </summary>
        private bool Committed = false;

        public DbContext Context { get; set; }

        public Repository() {
            Context = new PayContext();
#if DEBUG
            Context.Database.Log = (log) => {
                IocHelper.Get<ILogHelper>("sql_info").Info(log);
            };
#endif
        }

        public int Commit(params string[] tags) {
            int result = 0;
            try {
                result = this.Context.SaveChanges();
                if (!Committed) {
                    ClearCache(tags);
                } else {
                    if (tags != null)
                        cacheTags.AddRange(tags.ToList());
                }
            } catch (Exception ex) {
                StringBuilder errors = new StringBuilder();
                if (ex is DbEntityValidationException) {
                    var entityEx = ex as DbEntityValidationException;
                    foreach (var error in entityEx.EntityValidationErrors) {
                        foreach (var validError in error.ValidationErrors)
                            errors.AppendLine(validError.PropertyName + "=>" + validError.ErrorMessage);
                    }
                } else if (ex is System.Data.Entity.Core.UpdateException) {
                    foreach (DictionaryEntry error in ex.Data)
                        errors.AppendLine(error.Key + "=>" + error.Value);
                    errors.AppendLine(ex.Message);
                    var updateEx = ex as System.Data.Entity.Core.UpdateException;
                    errors.AppendLine(updateEx.InnerException.Message);
                } else if (ex is System.Data.Entity.Infrastructure.DbUpdateException) {
                    foreach (DictionaryEntry error in ex.Data)
                        errors.AppendLine(error.Key + "=>" + error.Value);
                    errors.AppendLine(ex.Message);
                    var updateEx = ex as System.Data.Entity.Infrastructure.DbUpdateException;
                    errors.AppendLine(updateEx.InnerException.Message);
                    errors.AppendLine(updateEx.GetBaseException().Message);
                } else {
                    foreach (DictionaryEntry error in ex.Data)
                        errors.AppendLine(error.Key + "=>" + error.Value);
                    errors.AppendLine(ex.Message);
                    errors.AppendLine(ex.GetBaseException().Message);
                }
                IocHelper.Get<ILogHelper>("data_error").Error(errors.ToString(), ex);
                throw new DataErrorException(errors.ToString(), ex);
            } finally {
                if (!Committed) {
                    //auditLastLog.Clear();
                    cacheTags.Clear();
                }
            }
            return result;
        }

        public void ClearCache(params string[] tags) {
            if (tags != null && tags.Length > 0) {
                var cache_tags = tags.Distinct();
                CacheHelper.Remove(cache_tags);
            }
        }

        /// <summary>
        /// 返回缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public T GetCache<T>(string key) {
            return CacheHelper.Get<T>(RequestMethod.tagKey + key);
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool RomoveCache(string key) {
            return CacheHelper.Remove(RequestMethod.tagKey + key);
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="seconds"></param>
        public void SetCache(string key, object value, int? seconds = null) {
            key = string.Format(RequestMethod.tagKey + key);
            CacheHelper.Set(key, value, seconds ?? 7200);
        }

        public void ErrorLog(Exception ex) {
            IocHelper.Get<ILogHelper>("data_error").Error(ex.GetBaseException().Message, ex);
        }

        public void Dispose() {
            if (Context != null) {
                Context.Dispose();
            }
            GC.SuppressFinalize(this);
        }
    }
}
